package cryptoutils

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"dgo/goutils/utils"
	"encoding/base64"
	"encoding/pem"
	"math"
	"os"
)

// 生成RSA私钥和公钥，返回
// bits 证书大小
func GenerateRSAKey(bits int) (privateKeyBase64 string, publicKeyBase64 string) {
	//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
	//Reader是一个全局、共享的密码用强随机数生成器
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		panic(err)
	}
	//保存私钥
	//通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
	X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)

	privateKeyBase64 = base64.StdEncoding.EncodeToString(X509PrivateKey)

	//保存公钥
	//获取公钥的数据
	publicKey := privateKey.PublicKey
	//X509对公钥编码
	X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
	if err != nil {
		publicKeyBase64 = ""
	}
	publicKeyBase64 = base64.StdEncoding.EncodeToString(X509PublicKey)
	return
}

// 生成RSA私钥和公钥，保存到文件中
// bits 证书大小
func GenerateRSAKey2File(bits int, privateKeyFile string, publicKeyFile string) error {
	//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
	//Reader是一个全局、共享的密码用强随机数生成器
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return err
	}
	//保存私钥
	//通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
	X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
	{
		privateFile, err := os.Create(privateKeyFile)
		if err != nil {
			return err
		}
		block := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
		pem.Encode(privateFile, &block)
	}

	//保存公钥
	//获取公钥的数据
	publicKey := privateKey.PublicKey
	//X509对公钥编码
	X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
	{
		pemFile, err := os.Create(publicKeyFile)
		if err != nil {
			return err
		}
		block := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
		pem.Encode(pemFile, &block)
	}
	return nil
}

func RSA_Base64FromPemFile(pemFile string) (string, error) {
	if len(pemFile) == 0 {
		return "", nil
	}
	buf, err := utils.ReadFile(pemFile)
	if err != nil {
		return "", err
	}
	block, _ := pem.Decode(buf)
	return base64.StdEncoding.EncodeToString(block.Bytes), nil
}

func RSA_PublicKeyFromFile(pemfile string) (*rsa.PublicKey, error) {
	if len(pemfile) == 0 {
		return nil, nil
	}
	buf, err := utils.ReadFile(pemfile)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(buf)
	return RSA_Bytes2PublicKey(block.Bytes)
}

func RSA_PrivateKeyFromFile(pemfile string) (*rsa.PrivateKey, error) {
	if len(pemfile) == 0 {
		return nil, nil
	}
	buf, err := utils.ReadFile(pemfile)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(buf)
	return RSA_Bytes2PrivateKey(block.Bytes)
}

func RSA_Base642PublicKey(publicKey string) (*rsa.PublicKey, error) {
	if len(publicKey) == 0 {
		return nil, nil
	}
	buf, err := base64.StdEncoding.DecodeString(publicKey)
	if err != nil {
		return nil, err
	}
	return RSA_Bytes2PublicKey(buf)
}

func RSA_Base642PrivateKey(privateKey string) (*rsa.PrivateKey, error) {
	if len(privateKey) == 0 {
		return nil, nil
	}
	buf, err := base64.StdEncoding.DecodeString(privateKey)
	if err != nil {
		return nil, err
	}
	return RSA_Bytes2PrivateKey(buf)
}

func RSA_Bytes2PublicKey(publicKeyBuf []byte) (*rsa.PublicKey, error) {
	publicKeyInterface, err := x509.ParsePKIXPublicKey(publicKeyBuf)
	if err != nil {
		return nil, err
	}
	return publicKeyInterface.(*rsa.PublicKey), nil
}

func RSA_Bytes2PrivateKey(privateKeyBuf []byte) (*rsa.PrivateKey, error) {
	privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBuf)
	if err != nil {
		return nil, err
	}
	return privateKey, nil
}

// RSA公钥加密
// srcData 要加密的数据
func RSA_EncryptPublicKey(srcData []byte, publicKey *rsa.PublicKey) ([]byte, error) {
	return rsa.EncryptPKCS1v15(rand.Reader, publicKey, srcData)
}

// RSA公钥加密
// srcData 要加密的数据
func RSA_EncryptPrivateKey(srcData []byte, publicKey *rsa.PublicKey) ([]byte, error) {

	return rsa.EncryptPKCS1v15(rand.Reader, publicKey, srcData)
}

// RSA私钥解密
//
//	cipherData 解密数据
func RSA_DecryptPrivateKey(cipherData []byte, privateKey *rsa.PrivateKey) ([]byte, error) {
	return rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherData)
}

/***
RSA非对称加密算法，基于PKCS#1规范, 我们在使用RSA的时候需要提供 公钥和私钥 ， 我们可以通过openss来为我们生成对应的pem格式的公钥和私钥匙。

关于pkcs相关标准如下,摘自百度:

PKCS#1：定义RSA公开密钥算法加密和签名机制，主要用于组织PKCS#7中所描述的数字签名和数字信封[22]。
PKCS#3：定义Diffie-Hellman密钥交换协议[23]。
PKCS#5：描述一种利用从口令派生出来的安全密钥加密字符串的方法。使用MD2或MD5 从口令中派生密钥，并采用DES-CBC模式加密。主要用于加密从一个计算机传送到另一个计算机的私人密钥，不能用于加密消息[24]。
PKCS#6：描述了公钥证书的标准语法，主要描述X.509证书的扩展格式[25]。
PKCS#7：定义一种通用的消息语法，包括数字签名和加密等用于增强的加密机制，PKCS#7与PEM兼容，所以不需其他密码操作，就可以将加密的消息转换成PEM消息[26]。
PKCS#8：描述私有密钥信息格式，该信息包括公开密钥算法的私有密钥以及可选的属性集等[27]。
PKCS#9：定义一些用于PKCS#6证书扩展、PKCS#7数字签名和PKCS#8私钥加密信息的属性类型[28]。
PKCS#10：描述证书请求语法[29]。
PKCS#11：称为Cyptoki，定义了一套独立于技术的程序设计接口，用于智能卡和PCMCIA卡之类的加密设备[30]。
PKCS#12：描述个人信息交换语法标准。描述了将用户公钥、私钥、证书和其他相关信息打包的语法[31]。
PKCS#13：椭圆曲线密码体制标准[32]。
PKCS#14：伪随机数生成标准。
PKCS#15：密码令牌信息格式标准[33]。
*/

func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

/*
*

	返回加密后的Base64
*/
func AesCBCEncryptBase64String(origData []byte, key []byte, iv []byte) (string, error) {
	crypted, err := AesCBCEncrypt(origData, key, iv)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(crypted), nil
}

func AddPrefixForWidth(src string, width int, prefix string) string {
	disl := width - len(src)
	if disl <= 0 {
		return src
	}
	fixl := len(prefix)
	if fixl == 0 {
		prefix = " "
		fixl = 1
	}

	if fixl == 1 {
		rep := bytes.Repeat([]byte(prefix), disl)
		str := string(rep) + src
		return str
	} else {
		l := int(math.Ceil(float64(disl) / float64(fixl)))
		rep := bytes.Repeat([]byte(prefix), l)
		str := string(rep[:disl]) + src
		return str
	}
}

func MakeAESCBCKey(orgkey string) []byte {
	l := len(orgkey)
	if l < 16 {
		orgkey = AddPrefixForWidth(orgkey, 16, "#$SF#EFUIOPOJFAEFC")
	} else if l < 24 {
		orgkey = AddPrefixForWidth(orgkey, 24, "#$DFEYEFASDFESXCVEAFCVES")
	} else if l < 32 {
		orgkey = AddPrefixForWidth(orgkey, 32, "#$DFEYEF9756345ASDF865774ESXCVEAFCVES")
	} else {
		orgkey = orgkey[:32]
	}
	return []byte(orgkey)
}

func AesCBCDecryptBase64String(crypted string, key []byte, iv []byte) ([]byte, error) {
	if len(crypted) == 0 {
		return nil, nil
	}
	cryptedBuf, err := base64.StdEncoding.DecodeString(crypted)
	if err != nil {
		return nil, err
	}

	return AesCBCDecrypt(cryptedBuf, key, iv)
}

func MakeDefaultIvFromKey(key []byte, blockSize int) (iv []byte) {
	return key[:blockSize]
}

/*
*

	key的长度必须是16的倍数

	AES加密，填充秘钥key的16位, 24, 32分别对应AES-128, AES-192, or AES-256.

	iv的长度是16的倍数
*/
func AesCBCEncrypt(origData, key []byte, iv []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	//blockSize := block.BlockSize()
	//iv = iv[:blockSize]

	origData = PKCS7Padding(origData, len(iv))
	blockMode := cipher.NewCBCEncrypter(block, iv)
	crypted := make([]byte, len(origData))
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

/*
*

	解密
	key的长度必须是16的倍数
*/
func AesCBCDecrypt(crypted, key []byte, iv []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	// blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, iv)
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	origData = PKCS7UnPadding(origData)
	return origData, nil
}
